/**
 * @author mrdoob / http://mrdoob.com/
 */

import * as THREE from '../../js/threejs/build/three.module.js'

import {UIPanel, UIRow, UIColor, UICheckbox, UIInteger, UITextArea, UIText, UINumber} from '../../js/threejs/libs/ui.js'
import {UIBoolean} from '../../js/threejs/libs/ui.three.js'

import {SetValueCommand} from '../../js/threejs/commands/SetValueCommand.js'
import {SetPositionCommand} from '../../js/threejs/commands/SetPositionCommand.js'
import {SetRotationCommand} from '../../js/threejs/commands/SetRotationCommand.js'
import {SetScaleCommand} from '../../js/threejs/commands/SetScaleCommand.js'
import {SetColorCommand} from '../../js/threejs/commands/SetColorCommand.js'

var SidebarObject = function (editor) {
  var strings = editor.strings

  var signals = editor.signals

  var container = new UIPanel()
  container.setBorderTop('0')
  container.setPaddingTop('20px')
  container.setDisplay('none')


  // type

  var objectTypeRow = new UIRow()
  var objectType = new UIText()

  objectTypeRow.add(new UIText(strings.getKey('sidebar/object/type')).setWidth('90px'))
  objectTypeRow.add(objectType)

  container.add(objectTypeRow)

  // file

  var objectFileRow = new UIRow()
  var objectFile = new UIText()

  objectFileRow.add(new UIText(strings.getKey('sidebar/object/file')).setWidth('90px'))
  objectFileRow.add(objectFile)

  container.add(objectFileRow)

  // uuid

  var objectUUIDRow = new UIRow()
  var objectUUID = new UIText()

  objectUUIDRow.add(new UIText(strings.getKey('sidebar/object/uuid')).setWidth('90px'))
  objectUUIDRow.add(objectUUID)

  container.add(objectUUIDRow)

  // name

  var objectNameRow = new UIRow()
  var objectName = new UIText()

  objectNameRow.add(new UIText(strings.getKey('sidebar/object/name')).setWidth('90px'))
  objectNameRow.add(objectName)

  container.add(objectNameRow)

  // position

  var objectPositionRow = new UIRow()
  var objectPositionX = new UINumber().setPrecision(3).setWidth('50px').onChange(update)
  var objectPositionY = new UINumber().setPrecision(3).setWidth('50px').onChange(update)
  var objectPositionZ = new UINumber().setPrecision(3).setWidth('50px').onChange(update)

  objectPositionRow.add(new UIText(strings.getKey('sidebar/object/position')).setWidth('90px'))
  objectPositionRow.add(objectPositionX, objectPositionY, objectPositionZ)

  container.add(objectPositionRow)

  // rotation

  var objectRotationRow = new UIRow()
  var objectRotationX = new UINumber().setStep(10).setNudge(0.1).setUnit('°').setWidth('50px').onChange(update)
  var objectRotationY = new UINumber().setStep(10).setNudge(0.1).setUnit('°').setWidth('50px').onChange(update)
  var objectRotationZ = new UINumber().setStep(10).setNudge(0.1).setUnit('°').setWidth('50px').onChange(update)

  objectRotationRow.add(new UIText(strings.getKey('sidebar/object/rotation')).setWidth('90px'))
  objectRotationRow.add(objectRotationX, objectRotationY, objectRotationZ)

  container.add(objectRotationRow)

  // scale

  var objectScaleRow = new UIRow()
  var objectScaleLock = new UICheckbox(true).setPosition('absolute').setLeft('75px')
  var objectScaleX = new UINumber(1).setPrecision(3).setRange(0.001, Infinity).setWidth('50px').onChange(updateScaleX)
  var objectScaleY = new UINumber(1).setPrecision(3).setRange(0.001, Infinity).setWidth('50px').onChange(updateScaleY)
  var objectScaleZ = new UINumber(1).setPrecision(3).setRange(0.001, Infinity).setWidth('50px').onChange(updateScaleZ)

  objectScaleRow.add(new UIText(strings.getKey('sidebar/object/scale')).setWidth('90px'))
  objectScaleRow.add(objectScaleLock)
  objectScaleRow.add(objectScaleX, objectScaleY, objectScaleZ)

  container.add(objectScaleRow)

  // fov

  var objectFovRow = new UIRow()
  var objectFov = new UINumber().onChange(update)

  objectFovRow.add(new UIText(strings.getKey('sidebar/object/fov')).setWidth('90px'))
  objectFovRow.add(objectFov)

  container.add(objectFovRow)

  // left

  var objectLeftRow = new UIRow()
  var objectLeft = new UINumber().onChange(update)

  objectLeftRow.add(new UIText(strings.getKey('sidebar/object/left')).setWidth('90px'))
  objectLeftRow.add(objectLeft)

  container.add(objectLeftRow)

  // right

  var objectRightRow = new UIRow()
  var objectRight = new UINumber().onChange(update)

  objectRightRow.add(new UIText(strings.getKey('sidebar/object/right')).setWidth('90px'))
  objectRightRow.add(objectRight)

  container.add(objectRightRow)

  // top

  var objectTopRow = new UIRow()
  var objectTop = new UINumber().onChange(update)

  objectTopRow.add(new UIText(strings.getKey('sidebar/object/top')).setWidth('90px'))
  objectTopRow.add(objectTop)

  container.add(objectTopRow)

  // bottom

  var objectBottomRow = new UIRow()
  var objectBottom = new UINumber().onChange(update)

  objectBottomRow.add(new UIText(strings.getKey('sidebar/object/bottom')).setWidth('90px'))
  objectBottomRow.add(objectBottom)

  container.add(objectBottomRow)

  // near

  var objectNearRow = new UIRow()
  var objectNear = new UINumber().onChange(update)

  objectNearRow.add(new UIText(strings.getKey('sidebar/object/near')).setWidth('90px'))
  objectNearRow.add(objectNear)

  container.add(objectNearRow)

  // far

  var objectFarRow = new UIRow()
  var objectFar = new UINumber().onChange(update)

  objectFarRow.add(new UIText(strings.getKey('sidebar/object/far')).setWidth('90px'))
  objectFarRow.add(objectFar)

  container.add(objectFarRow)

  // intensity

  var objectIntensityRow = new UIRow()
  var objectIntensity = new UINumber().setRange(0, Infinity).onChange(update)

  objectIntensityRow.add(new UIText(strings.getKey('sidebar/object/intensity')).setWidth('90px'))
  objectIntensityRow.add(objectIntensity)

  container.add(objectIntensityRow)

  // color

  var objectColorRow = new UIRow()
  var objectColor = new UIColor().onChange(update)

  objectColorRow.add(new UIText(strings.getKey('sidebar/object/color')).setWidth('90px'))
  objectColorRow.add(objectColor)

  container.add(objectColorRow)

  // ground color

  var objectGroundColorRow = new UIRow()
  var objectGroundColor = new UIColor().onChange(update)

  objectGroundColorRow.add(new UIText(strings.getKey('sidebar/object/groundcolor')).setWidth('90px'))
  objectGroundColorRow.add(objectGroundColor)

  container.add(objectGroundColorRow)

  // distance

  var objectDistanceRow = new UIRow()
  var objectDistance = new UINumber().setRange(0, Infinity).onChange(update)

  objectDistanceRow.add(new UIText(strings.getKey('sidebar/object/distance')).setWidth('90px'))
  objectDistanceRow.add(objectDistance)

  container.add(objectDistanceRow)

  // angle

  var objectAngleRow = new UIRow()
  var objectAngle = new UINumber()
    .setPrecision(3)
    .setRange(0, Math.PI / 2)
    .onChange(update)

  objectAngleRow.add(new UIText(strings.getKey('sidebar/object/angle')).setWidth('90px'))
  objectAngleRow.add(objectAngle)

  container.add(objectAngleRow)

  // penumbra

  var objectPenumbraRow = new UIRow()
  var objectPenumbra = new UINumber().setRange(0, 1).onChange(update)

  objectPenumbraRow.add(new UIText(strings.getKey('sidebar/object/penumbra')).setWidth('90px'))
  objectPenumbraRow.add(objectPenumbra)

  container.add(objectPenumbraRow)

  // decay

  var objectDecayRow = new UIRow()
  var objectDecay = new UINumber().setRange(0, Infinity).onChange(update)

  objectDecayRow.add(new UIText(strings.getKey('sidebar/object/decay')).setWidth('90px'))
  objectDecayRow.add(objectDecay)

  container.add(objectDecayRow)

  // shadow

  var objectShadowRow = new UIRow()

  objectShadowRow.add(new UIText(strings.getKey('sidebar/object/shadow')).setWidth('90px'))

  var objectCastShadow = new UIBoolean(false, strings.getKey('sidebar/object/cast')).onChange(update)
  objectShadowRow.add(objectCastShadow)

  var objectReceiveShadow = new UIBoolean(false, strings.getKey('sidebar/object/receive')).onChange(update)
  objectShadowRow.add(objectReceiveShadow)

  container.add(objectShadowRow)

  // shadow radius

  var objectShadowRadiusRow = new UIRow()

  objectShadowRadiusRow.add(new UIText(strings.getKey('sidebar/object/shadowRadius')).setWidth('90px'))

  var objectShadowRadius = new UINumber(1).onChange(update)
  objectShadowRadiusRow.add(objectShadowRadius)

  container.add(objectShadowRadiusRow)

  // shadow bias

  var objectShadowBiasRow = new UIRow()

  objectShadowBiasRow.add(new UIText(strings.getKey('sidebar/object/shadowBias')).setWidth('90px'))

  var objectShadowBias = new UINumber(0).setPrecision(6).setStep(0.001).setNudge(0.000001).onChange(update)
  objectShadowBiasRow.add(objectShadowBias)

  container.add(objectShadowBiasRow)

  // visible

  var objectVisibleRow = new UIRow()
  var objectVisible = new UICheckbox().onChange(update)

  objectVisibleRow.add(new UIText(strings.getKey('sidebar/object/visible')).setWidth('90px'))
  objectVisibleRow.add(objectVisible)

  container.add(objectVisibleRow)

  // frustumCulled

  var objectFrustumCulledRow = new UIRow()
  var objectFrustumCulled = new UICheckbox().onChange(update)

  objectFrustumCulledRow.add(new UIText(strings.getKey('sidebar/object/frustumcull')).setWidth('90px'))
  objectFrustumCulledRow.add(objectFrustumCulled)

  container.add(objectFrustumCulledRow)

  // renderOrder

  var objectRenderOrderRow = new UIRow()
  var objectRenderOrder = new UIInteger().setWidth('50px').onChange(update)

  objectRenderOrderRow.add(new UIText(strings.getKey('sidebar/object/renderorder')).setWidth('90px'))
  objectRenderOrderRow.add(objectRenderOrder)

  container.add(objectRenderOrderRow)

  // user data

  var objectUserDataRow = new UIRow()
  var objectUserData = new UITextArea().setWidth('150px').setHeight('40px').setFontSize('12px').onChange(update)
  objectUserData.onKeyUp(function () {
    try {
      JSON.parse(objectUserData.getValue())

      objectUserData.dom.classList.add('success')
      objectUserData.dom.classList.remove('fail')
    } catch (error) {
      objectUserData.dom.classList.remove('success')
      objectUserData.dom.classList.add('fail')
    }
  })

  objectUserDataRow.add(new UIText(strings.getKey('sidebar/object/userdata')).setWidth('90px'))
  objectUserDataRow.add(objectUserData)

  container.add(objectUserDataRow)

  //

  function updateScaleX() {
    var object = editor.selected

    if (objectScaleLock.getValue() === true) {
      var scale = objectScaleX.getValue() / object.scale.x

      objectScaleY.setValue(objectScaleY.getValue() * scale)
      objectScaleZ.setValue(objectScaleZ.getValue() * scale)
    }

    update()
  }

  function updateScaleY() {
    var object = editor.selected

    if (objectScaleLock.getValue() === true) {
      var scale = objectScaleY.getValue() / object.scale.y

      objectScaleX.setValue(objectScaleX.getValue() * scale)
      objectScaleZ.setValue(objectScaleZ.getValue() * scale)
    }

    update()
  }

  function updateScaleZ() {
    var object = editor.selected

    if (objectScaleLock.getValue() === true) {
      var scale = objectScaleZ.getValue() / object.scale.z

      objectScaleX.setValue(objectScaleX.getValue() * scale)
      objectScaleY.setValue(objectScaleY.getValue() * scale)
    }

    update()
  }

  function update() {
    var object = editor.selected

    if (object !== null) {
      var newPosition = new THREE.Vector3(objectPositionX.getValue(), objectPositionY.getValue(), objectPositionZ.getValue())
      if (object.position.distanceTo(newPosition) >= 0.01) {
        editor.execute(new SetPositionCommand(editor, object, newPosition))
      }

      var newRotation = new THREE.Euler(
        objectRotationX.getValue() * THREE.MathUtils.DEG2RAD,
        objectRotationY.getValue() * THREE.MathUtils.DEG2RAD,
        objectRotationZ.getValue() * THREE.MathUtils.DEG2RAD
      )
      if (object.rotation.toVector3().distanceTo(newRotation.toVector3()) >= 0.01) {
        editor.execute(new SetRotationCommand(editor, object, newRotation))
      }

      var newScale = new THREE.Vector3(objectScaleX.getValue(), objectScaleY.getValue(), objectScaleZ.getValue())
      if (object.scale.distanceTo(newScale) >= 0.01) {
        editor.execute(new SetScaleCommand(editor, object, newScale))
      }

      if (object.fov !== undefined && Math.abs(object.fov - objectFov.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'fov', objectFov.getValue()))
        object.updateProjectionMatrix()
      }

      if (object.left !== undefined && Math.abs(object.left - objectLeft.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'left', objectLeft.getValue()))
        object.updateProjectionMatrix()
      }

      if (object.right !== undefined && Math.abs(object.right - objectRight.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'right', objectRight.getValue()))
        object.updateProjectionMatrix()
      }

      if (object.top !== undefined && Math.abs(object.top - objectTop.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'top', objectTop.getValue()))
        object.updateProjectionMatrix()
      }

      if (object.bottom !== undefined && Math.abs(object.bottom - objectBottom.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'bottom', objectBottom.getValue()))
        object.updateProjectionMatrix()
      }

      if (object.near !== undefined && Math.abs(object.near - objectNear.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'near', objectNear.getValue()))
        if (object.isOrthographicCamera) {
          object.updateProjectionMatrix()
        }
      }

      if (object.far !== undefined && Math.abs(object.far - objectFar.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'far', objectFar.getValue()))
        if (object.isOrthographicCamera) {
          object.updateProjectionMatrix()
        }
      }

      if (object.intensity !== undefined && Math.abs(object.intensity - objectIntensity.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'intensity', objectIntensity.getValue()))
      }

      if (object.color !== undefined && object.color.getHex() !== objectColor.getHexValue()) {
        editor.execute(new SetColorCommand(editor, object, 'color', objectColor.getHexValue()))
      }

      if (object.groundColor !== undefined && object.groundColor.getHex() !== objectGroundColor.getHexValue()) {
        editor.execute(new SetColorCommand(editor, object, 'groundColor', objectGroundColor.getHexValue()))
      }

      if (object.distance !== undefined && Math.abs(object.distance - objectDistance.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'distance', objectDistance.getValue()))
      }

      if (object.angle !== undefined && Math.abs(object.angle - objectAngle.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'angle', objectAngle.getValue()))
      }

      if (object.penumbra !== undefined && Math.abs(object.penumbra - objectPenumbra.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'penumbra', objectPenumbra.getValue()))
      }

      if (object.decay !== undefined && Math.abs(object.decay - objectDecay.getValue()) >= 0.01) {
        editor.execute(new SetValueCommand(editor, object, 'decay', objectDecay.getValue()))
      }

      if (object.visible !== objectVisible.getValue()) {
        editor.execute(new SetValueCommand(editor, object, 'visible', objectVisible.getValue()))
      }

      if (object.frustumCulled !== objectFrustumCulled.getValue()) {
        editor.execute(new SetValueCommand(editor, object, 'frustumCulled', objectFrustumCulled.getValue()))
      }

      if (object.renderOrder !== objectRenderOrder.getValue()) {
        editor.execute(new SetValueCommand(editor, object, 'renderOrder', objectRenderOrder.getValue()))
      }

      if (object.castShadow !== undefined && object.castShadow !== objectCastShadow.getValue()) {
        editor.execute(new SetValueCommand(editor, object, 'castShadow', objectCastShadow.getValue()))
      }

      if (object.receiveShadow !== undefined && object.receiveShadow !== objectReceiveShadow.getValue()) {
        if (object.material !== undefined) object.material.needsUpdate = true
        editor.execute(new SetValueCommand(editor, object, 'receiveShadow', objectReceiveShadow.getValue()))
      }

      if (object.shadow !== undefined) {
        if (object.shadow.radius !== objectShadowRadius.getValue()) {
          editor.execute(new SetValueCommand(editor, object.shadow, 'radius', objectShadowRadius.getValue()))
        }

        if (object.shadow.bias !== objectShadowBias.getValue()) {
          editor.execute(new SetValueCommand(editor, object.shadow, 'bias', objectShadowBias.getValue()))
        }
      }

      try {
        var userData = JSON.parse(objectUserData.getValue())
        if (JSON.stringify(object.userData) != JSON.stringify(userData)) {
          editor.execute(new SetValueCommand(editor, object, 'userData', userData))
        }
      } catch (exception) {
        console.warn(exception)
      }
    }
  }

  function updateRows(object) {
    var properties = {
      fov: objectFovRow,
      left: objectLeftRow,
      right: objectRightRow,
      top: objectTopRow,
      bottom: objectBottomRow,
      near: objectNearRow,
      far: objectFarRow,
      intensity: objectIntensityRow,
      color: objectColorRow,
      groundColor: objectGroundColorRow,
      distance: objectDistanceRow,
      angle: objectAngleRow,
      penumbra: objectPenumbraRow,
      decay: objectDecayRow,
      castShadow: objectShadowRow,
      receiveShadow: objectReceiveShadow,
      shadow: [objectShadowRadiusRow, objectShadowBiasRow],
    }

    for (var property in properties) {
      var uiElement = properties[property]

      if (Array.isArray(uiElement) === true) {
        for (var i = 0; i < uiElement.length; i++) {
          uiElement[i].setDisplay(object[property] !== undefined ? '' : 'none')
        }
      } else {
        uiElement.setDisplay(object[property] !== undefined ? '' : 'none')
      }
    }
  }

  function updateTransformRows(object) {
    if (object.isLight || (object.isObject3D && object.userData.targetInverse)) {
      objectRotationRow.setDisplay('none')
      objectScaleRow.setDisplay('none')
    } else {
      objectRotationRow.setDisplay('')
      objectScaleRow.setDisplay('')
    }
  }

  // events

  signals.objectSelected.add(function (object) {
    if (object !== null) {
      container.setDisplay('block')

      updateRows(object)
      updateUI(object)
    } else {
      container.setDisplay('none')
    }
  })

  signals.objectChanged.add(function (object) {
    if (object !== editor.selected) return

    updateUI(object)
  })

  signals.refreshSidebarObject3D.add(function (object) {
    if (object !== editor.selected) return

    updateUI(object)
  })

  /**
   * zhouwr add by 2020-10-30 16:33
   * 模型对象保存到后台
   */
  signals.objectRemoteSaved.add(function () {
    editor.storageRemote.save()
  })

  function updateUI(object) {
    objectType.setValue(object.type)

    objectFile.setValue(object.file)

    objectUUID.setValue(object.uuid)
    objectName.setValue(object.name)

    objectPositionX.setValue(object.position.x)
    objectPositionY.setValue(object.position.y)
    objectPositionZ.setValue(object.position.z)

    objectRotationX.setValue(object.rotation.x * THREE.MathUtils.RAD2DEG)
    objectRotationY.setValue(object.rotation.y * THREE.MathUtils.RAD2DEG)
    objectRotationZ.setValue(object.rotation.z * THREE.MathUtils.RAD2DEG)

    objectScaleX.setValue(object.scale.x)
    objectScaleY.setValue(object.scale.y)
    objectScaleZ.setValue(object.scale.z)

    if (object.fov !== undefined) {
      objectFov.setValue(object.fov)
    }

    if (object.left !== undefined) {
      objectLeft.setValue(object.left)
    }

    if (object.right !== undefined) {
      objectRight.setValue(object.right)
    }

    if (object.top !== undefined) {
      objectTop.setValue(object.top)
    }

    if (object.bottom !== undefined) {
      objectBottom.setValue(object.bottom)
    }

    if (object.near !== undefined) {
      objectNear.setValue(object.near)
    }

    if (object.far !== undefined) {
      objectFar.setValue(object.far)
    }

    if (object.intensity !== undefined) {
      objectIntensity.setValue(object.intensity)
    }

    if (object.color !== undefined) {
      objectColor.setHexValue(object.color.getHexString())
    }

    if (object.groundColor !== undefined) {
      objectGroundColor.setHexValue(object.groundColor.getHexString())
    }

    if (object.distance !== undefined) {
      objectDistance.setValue(object.distance)
    }

    if (object.angle !== undefined) {
      objectAngle.setValue(object.angle)
    }

    if (object.penumbra !== undefined) {
      objectPenumbra.setValue(object.penumbra)
    }

    if (object.decay !== undefined) {
      objectDecay.setValue(object.decay)
    }

    if (object.castShadow !== undefined) {
      objectCastShadow.setValue(object.castShadow)
    }

    if (object.receiveShadow !== undefined) {
      objectReceiveShadow.setValue(object.receiveShadow)
    }

    if (object.shadow !== undefined) {
      objectShadowRadius.setValue(object.shadow.radius)
      objectShadowBias.setValue(object.shadow.bias)
    }

    objectVisible.setValue(object.visible)
    objectFrustumCulled.setValue(object.frustumCulled)
    objectRenderOrder.setValue(object.renderOrder)

    try {
      objectUserData.setValue(JSON.stringify(object.userData, null, '  '))
    } catch (error) {
      console.log(error)
    }

    objectUserData.setBorderColor('transparent')
    objectUserData.setBackgroundColor('')

    updateTransformRows(object)
  }

  return container
}

export {SidebarObject}
